//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
package com.saasovation.common.port.adapter.persistence;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.lang.NonNull;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public abstract class AbstractQueryService {

    private final NamedParameterJdbcTemplate jdbcTemplate;

    protected AbstractQueryService(NamedParameterJdbcTemplate jdbcTemplate) {
        super();
        this.jdbcTemplate = jdbcTemplate;
    }

    protected <T> T queryObject(Class<T> aClass,
                                String sql,
                                JoinOn aJoinOn,
                                Map<String, Object> params) {
        return this.jdbcTemplate.query(sql, params, rs -> {
            boolean next = rs.next();
            if (next) {
                return mapResultToType(rs, aClass, aJoinOn);
            }
            return null;
        });
    }

    protected <T> Collection<T> queryObjects(Class<T> aClass,
                                             String sql,
                                             JoinOn aJoinOn,
                                             Map<String, Object> params) {
        return this.jdbcTemplate.query(sql, params,
                new RowMapperResultSetExtractor<>(new BeanPropertyRowMapper<>()) {
                    @Override
                    public List<T> extractData(@NonNull ResultSet rs) throws SQLException {
                        List<T> results = new ArrayList<>();
                        while (rs.next()) {
                            T object = mapResultToType(rs, aClass, aJoinOn);
                            results.add(object);
                        }
                        return results;
                    }
                });
    }

    protected String queryString(String sql, Map<String, Object> params) {
        return this.jdbcTemplate.query(sql, params, rs -> {
            boolean next = rs.next();
            if (next) {
                return rs.getString(1);
            }
            return null;
        });
    }

    private <T> T mapResultToType(ResultSet aResultSet, Class<T> aClass, JoinOn aJoinOn) {
        ResultSetObjectMapper<T> mapper = new ResultSetObjectMapper<>(aResultSet, aClass, aJoinOn);
        return mapper.mapResultToType();
    }
}
